#include "stdafx.h"
#include "shark.h"

#define MODELNAME1 "data/leopard_shark_opt_noeye.txt"
#define MODELNAME2 "data/mezirozame.txt"

#define PATH_LENGTH 5.0f
#define SALMON_PATH_LENGTH 1.5f
#define SHARK_PATH_LENGTH 5.0f
#define DOLPHIN_PATH_LENGTH 4.0f
#define SHARK_SPEED 2.0f	// so, it will finish the path in PATH_LENGTH secs

#pragma warning(disable : 4996)


FishModel::FishModel()
{
	Clear();

}



FishModel::~FishModel()
{
	Clear();
}	


void FishModel::Clear()
{
	m_meshes.clear();
	m_materials.clear();
}


char modelname[32];
char path[MAX_PATH];

bool FishModel::LoadFromAsciiMilkshape(const char* filename)
{
	Clear();

	FILE* pFile;
	int numMeshes,numMaterials;
	// int numJoints;


	const char* ch = strrchr(filename,'/');
	int len = int(ch-filename+1);
	strncpy(path,filename,len);
	strcpy(modelname,filename+len);

	pFile =  fopen(filename,"r");
	if (pFile==NULL)
	{
		return false;
	}

	char buffer[255];

	// keep reading till the end of the file...
	while(!feof(pFile))
	{
		fgets(buffer,255,pFile);

		// skip empty lines (with only newline characters)
		if( strlen(buffer)<2)
			continue;
		// skip comments
		if( strncmp(buffer,"//",2)==0)
			continue;
		// don't read bone information.....
		if( _strnicmp(buffer,"bones",5)==0 )
			continue;

		// skip frames for now
		if( _strnicmp(buffer,"frame",5)==0 )
			continue;

		// read the meshes
		if(sscanf(buffer,"Meshes: %d",&numMeshes)==1)
		{
			if(numMeshes==0)
				continue;
			else
			{
				if(!ReadMeshesMilkshapeAscii(pFile,numMeshes))
					return false;
				continue;
			}
		}

		// read the materials
		if(sscanf(buffer,"Materials: %d",&numMaterials)==1)
		{
			if(numMaterials==0)
				continue;
			else
				if(!ReadMaterialsMilkshapeAscii(pFile,numMaterials))
					return false;
		}
	}

	return true;
}


bool FishModel::ReadMeshesMilkshapeAscii(FILE* pFile,int numMeshes)
{
	//float minX,minY,minZ,maxX,maxY,maxZ;
	char name[32];	//dummy name
	int flag,bone,materialIndex,smooth_group;
	float x, y, z, s, t;

	minX = minY = minZ = 100000;
	maxX = maxY = maxZ = -100000;
	maxMeshVerts=0;

	// allocate space for the meshes


	// read the vertices
	for (int i=0; i<numMeshes; i++)
	{
		Mesh mesh;

		fscanf(pFile,"%s %d %d",name,&flag,&materialIndex);
		strncpy(mesh.m_name,name+1,strlen(name)-2);
		mesh.m_name[strlen(name)-2]=0;
		mesh.m_materialIndex = materialIndex;

		int numVertices;
		fscanf(pFile,"%d",&numVertices);

		if(numVertices>maxMeshVerts)
			maxMeshVerts=numVertices;
		
		for (int j=0; j<numVertices; j++)
		{
			Vertex vert;

			fscanf(pFile,"%d %f %f %f %f %f %d",
				&flag,&x,&y,&z,&s,&t,&bone);
			//check mins/maxes
			if(minX>x) minX=x;
			if(minY>y) minY=y;
			if(minZ>z) minZ=z;
			if(maxX<x) maxX=x;
			if(maxY<y) maxY=y;
			if(maxZ<z) maxZ=z;

			vert.m_location.Set(x,y,z);
			vert.m_texCoord.Set(s,1-t);
			
			mesh.m_vertices.push_back( vert );
			/*mesh->m_vertexIndices.push_back( 
				(int)m_vertices.size()-1 );*/
		}

		//
		// read normals number
		int numNormals;
		fscanf(pFile,"%d",&numNormals);

		for (int j=0; j<numNormals; j++)
		{
			fscanf(pFile,"%f %f %f",&x,&y,&z);
			Vector3 normal(x,y,z);

			mesh.m_normals.push_back(normal);
			/*mesh.m_normalIndices.push_back(
				(int)m_normals.size()-1 );*/
		}

		//
		// read triangles number
		int numTriangles;
		fscanf(pFile,"%d",&numTriangles);

		for (int j=0; j<numTriangles; j++)
		{
			Triangle tri;

			fscanf(pFile,"%d %d %d %d %d %d %d %d",
				&flag, 
				&tri.m_vertexIndices[0],
				&tri.m_vertexIndices[1],
				&tri.m_vertexIndices[2],
				&tri.m_normalIndices[0],
				&tri.m_normalIndices[1],
				&tri.m_normalIndices[2],
				&smooth_group);

			mesh.m_triangles.push_back(tri);
			/*pmesh->m_triangleIndices.push_back(
				(int)m_triangles.size()-1);*/
		}

		m_meshes.push_back(mesh);
	}
	return true;
}


bool FishModel::ReadMaterialsMilkshapeAscii(FILE* pFile,int numMaterials)
{
	char buffer[512];
	for (int m=0; m<numMaterials; m++)
	{
		Material mat;

		//read the material name
		fscanf(pFile,"%s",buffer);	//buffer now contains the name with quotes
		strncpy(mat.m_name,buffer+1,strlen(buffer)-2); //copy the name without quotes
		mat.m_name[strlen(buffer)-2]=0; //null-terminated

		//read ambient values
		fscanf(pFile,"%f %f %f %f",
			&mat.m_ambient[0],
			&mat.m_ambient[1],
			&mat.m_ambient[2],
			&mat.m_ambient[3]);

		//read diffuse
		fscanf(pFile,"%f %f %f %f",
			&mat.m_diffuse[0],
			&mat.m_diffuse[1],
			&mat.m_diffuse[2],
			&mat.m_diffuse[3]);

		//specular
		fscanf(pFile,"%f %f %f %f",
			&mat.m_specular[0],
			&mat.m_specular[1],
			&mat.m_specular[2],
			&mat.m_specular[3]);

		// emissive
		fscanf(pFile,"%f %f %f %f",
			&mat.m_emissive[0],
			&mat.m_emissive[1],
			&mat.m_emissive[2],
			&mat.m_emissive[3]);

		// shininess
		fscanf(pFile,"%f",&mat.m_shininess);
		//transparency
		fscanf(pFile,"%f",&mat.m_transparency);

		//
		//color map (the texture)
		char pTextureFilename[32];
		fscanf(pFile,"%s",buffer);
		// copy the texture filename without the quotes
		strncpy(pTextureFilename,buffer+1,strlen(buffer)-2); 
		pTextureFilename[strlen(buffer)-2]=0; //null-terminated

		//
		//alpha map
		fscanf(pFile,"%s",buffer); //skip alpha map...
		//strncpy(mat.m_alphaTextureFilename,buffer+1,strlen(buffer)-2);
		//mat.m_alphaTextureFilename[strlen(buffer)-2]=0; //null-terminated		

		strcpy(buffer,path);
		if (strlen(pTextureFilename)>0)
		{
			// if texture name starts with a ".\", skip it
			if(strncmp(pTextureFilename,".\\",2)==0)
				strcat(buffer,pTextureFilename+2);
			else
				strcat(buffer,pTextureFilename);
		}	

		//HTexture htex = GetTexture(buffer);
		//mat.m_texture = htex;
		mat.m_texture = LoadTexture(buffer);
		m_materials.push_back(mat);
	}

	
	// update the mesh vector in each material
	// so each material knows which meshes to draw

	/*for (int m=0; m<(int)m_meshes.size(); m++)
	{
		m_materials[m_meshes[m].m_materialIndex].m_meshIndexes.push_back(m);
	}*/ // NOTE, not necesary for the shark. Each sark has one material only

	return true;
}


void FishModel::ApplyMaterial(int index)
{
	/*assert(0<=index && index<(int)m_materials.size());
	const Material* pmat = m_materials[index];

	glMaterialfv(GL_FRONT,	GL_AMBIENT,		pmat->m_ambient);
	glMaterialfv(GL_FRONT,	GL_DIFFUSE,		pmat->m_diffuse);
	glMaterialfv(GL_FRONT,	GL_SPECULAR,	pmat->m_specular);
	glMaterialfv(GL_FRONT,	GL_EMISSION,	pmat->m_emissive);
	glMaterialf (GL_FRONT,	GL_SHININESS,	pmat->m_shininess);

	glColor4fv(pmat->m_diffuse);

	if(pmat->m_textureIndex<0)
	{
		glDisable(GL_TEXTURE_2D);
	}
	else
	{
		glEnable(GL_TEXTURE_2D);
		BindTexture(m_textures[pmat->m_textureIndex]);

		glEnableClientState( GL_TEXTURE_COORD_ARRAY );
		glTexCoordPointer(2,GL_FLOAT,sizeof(Vector2),&texCoordList[0].x);
	}*/
}





Fish::Fish(FishManager* sm)
:	mpContainer(sm),
	mbShowWireframe(false),
	mRootIndex(0),
	//mBezierSpeed(0.002f),
	mTimeElapsed(BIG_REAL),	// so before first move, the shark must think
	mDestTime(4),	// there must be 4 secs before reaching target
	mAngleFactor(0),
	mbFirstTime(true),
	mCurrentAction(Cruise)
{
	mBezierSpeed = SHARK_SPEED/PATH_LENGTH;
	m_bShowBones=true;

	mbLeftEyed = (rand()>(RAND_MAX/2));	// 50% to be left eyed, 50% to be right-eyed
}


Fish::~Fish()
{
	Clear();
}


void Fish::Clear()
{
	mBoneCount=0;
	DropAllBones();
	mLength=0;
	mModelName="";
	mRootIndex=0;
	delete [] mpLinks;
//	delete [] m_pweights;
}

/*
 *	
 */
void Fish::Build(const FishModel* pmodel,const char* modelname, 
					   int numSegments,int rootIndex,float* lengthPerSegment)
{
	
	mModelName = modelname;

	float totalSegmentLength(0);		// sum of all lengthPerSegment[...]
	
	int i;							// loop var

	mBoneCount = numSegments+1;
	if(mBoneCount<1)
		return;

	if(pmodel==NULL)
		return;

	//
	// the length of the model (from Z to -Z)
	float meshLength = pmodel->maxZ-pmodel->minZ;
	
	//
	// init the bounding sphere
	mBoundingSphere.m_radius = meshLength*0.65f;
	mBoundingSphere.m_center.z = pmodel->maxZ-meshLength*0.5f;

	
	mRootIndex = rootIndex;

	this->SetRotation(0,0,1,0);

	//
	// sum the length per segment
	for(i=0; i<numSegments; i++)
		totalSegmentLength += lengthPerSegment[i];

	//
	// put in absLengthPerSgment the absolute length each segment will have
	// according to the real length of the model and the relative lengthPerSegment
	float *absLengthPerSegment = new float[numSegments];	
	for(i=0; i<numSegments; i++)
		absLengthPerSegment[i] = (lengthPerSegment[i]/totalSegmentLength)*meshLength;

	//
	// add bones to the system (just default bones)
	for(i=0; i<mBoneCount; i++)
	{
		AddBone(Bone());
	}

	//
	// set root bone attributes
	// root is a virtual bone somewhere at the center of the shark
	// and it does not take part in the static animation...
	Vector3 vcurEnd;
	Bone* proot = GetBone(rootIndex);
	proot->SetParent(NULL);	//no parent for the root

	float z(pmodel->maxZ);	// the near edge of the model	
	for(i=0; i<rootIndex; i++)	
		z -= absLengthPerSegment[i];
	//z -= (absLengthPerSegment[rootIndex]/2.f);

	vcurEnd.Set(0,0,z);
	proot->mBase = vcurEnd;
	proot->mEnd = vcurEnd;	// same end as the base
	proot->SetLength(0);
	this->SetPosition(vcurEnd);	// position of the root is the position of
								// the whole system
	//vcurEnd += Vector3(0.f,0.f,absLengthPerSegment[rootIndex]/2.f);

	//
	// walk from root to near Z
	Bone *parent(proot);
	for (i=rootIndex-1; i>=0; i--)
	{
		Bone* pb = GetBone(i);
		pb->SetParent(i+1);	// set previous bone as parent
		pb->SetBase(vcurEnd);
		pb->SetLength(absLengthPerSegment[i]);
		vcurEnd += Vector3(0.f,0.f,absLengthPerSegment[i]);
		pb->mEnd = vcurEnd;
		parent = pb;		
	}

	//
	// now walk from root to far -Z
	parent = proot;
	vcurEnd = proot->mBase;
	for (i=rootIndex+1; i<mBoneCount; i++)
	{
		Bone *pb = GetBone(i);
		pb->SetParent(i-1);
		pb->SetBase(vcurEnd);
		pb->SetLength(-absLengthPerSegment[i-1]);
		vcurEnd -= Vector3(0.f,0.f,absLengthPerSegment[i-1]);
		pb->mEnd = vcurEnd;
		parent = pb;		
	}

//#define LOG_BONES
#ifdef LOG_BONES
	Log("-----------------------------------------------------\n");
	Log("Just created the bones.....\n");

	Bone* pbone(GetBone(mRootIndex));
	Log("root bone (index %d): base(%.3f,%.3f,%3f), length=%.3f\n",
		mRootIndex,
		pbone->mBase.x,pbone->mBase.y,pbone->mBase.z,pbone->mLength);
	int b;
	for (b=mRootIndex-1; b>=0; b--)
	{
		pbone = GetBone(b);
		Log("bone (index %d): base(%.3f,%.3f,%3f), length=%.3f\n",
			b,
			pbone->mBase.x,pbone->mBase.y,pbone->mBase.z,pbone->mLength);
	}
	for (b=mRootIndex+1; b<mBoneCount; b++)
	{
		pbone = GetBone(b);
		Log("bone (index %d): base(%.3f,%.3f,%3f), length=%.3f\n",
			b,
			pbone->mBase.x,pbone->mBase.y,pbone->mBase.z,pbone->mLength);
	}
	Log("-----------------------------------------------------\n");
#endif


	int numMeshes = (int)pmodel->m_meshes.size();
    mpLinks = new MeshBoneMap[mBoneCount];
	
	
	//
	// assign vertices to bones
	
	for(int m=0; m<numMeshes; m++)
	{
		const FishModel::Mesh &mesh = pmodel->m_meshes[m];

		if(strcmp(mesh.m_name,"body")==0 || strcmp(mesh.m_name,"tail")==0)
		{
			for (i=0; i<(int)mesh.m_vertices.size(); i++)
			{
				const FishModel::Vertex &vert = mesh.m_vertices[i];
				float vz = vert.m_location.z;	

				if(vz > GetBone(rootIndex)->mBase.z)
				{
					for (int b=rootIndex-1; b>=0; b--)
					{
						if(b==0 || vz <= GetBone(b)->mEnd.z)
						{
							float p1( (vz-GetBone(b)->mBase.z)/GetBone(b)->mLength);
							float p2(p1*p1);

							float bias1(p2*0.5f);
							float bias2(-p2+p1+0.5f);
							float bias3(0.5f*p2-p1+0.5f);

							// next bone (closer to root)
							if(b!=rootIndex-1)
								mpLinks[b+1][m].push_back(BoneVertexLink(b+1,i,bias3));

							// current bone
							mpLinks[b][m].push_back(BoneVertexLink(b,i,bias2));

							// previous link (closer to user)
							if(b>0)
								mpLinks[b][m].push_back(BoneVertexLink(b,i,bias1));

							break;
						}
					}
				}
				else
				{
					for (int b=rootIndex+1; b<mBoneCount; b++)
					{
						if(b==(mBoneCount-1) || vz >= GetBone(b)->mEnd.z)
						{
							float p1( (vz-GetBone(b)->mBase.z)/GetBone(b)->mLength );
							float p2(p1*p1);

							float bias1(p2*0.5f);
							float bias2(-p2+p1+0.5f);
							float bias3(0.5f*p2-p1+0.5f);
						
							// next bone (closer to user)
							if(b!=mBoneCount-1)
								mpLinks[b][m].push_back(BoneVertexLink(b,i,bias3));

							// current bone
							mpLinks[b][m].push_back(BoneVertexLink(b,i,bias2));

							// previous link (closer to root)
							if(b!=rootIndex+1)
								mpLinks[b][m].push_back(BoneVertexLink(b,i,bias1));
							break;
						}
					}
				}			
			}
		}
		else	// any other mesh
		{
			for (i=0; i<(int)mesh.m_vertices.size(); i++)
			{
				const FishModel::Vertex &vert = mesh.m_vertices[i];
				float vz = vert.m_location.z;	

				if(vz > GetBone(rootIndex)->mBase.z)
				{
					for (int b=rootIndex-1; b>=0; b--)
					{
						if(vz < GetBone(b)->mEnd.z)
						{
							// current bone
							mpLinks[b][m].push_back(BoneVertexLink(b,i,1.f));
							break;
						}
					}
				}
				else
				{
					for (int b=rootIndex+1; b<mBoneCount; b++)
					{
						if(vz > GetBone(b)->mEnd.z)
						{
							// current bone
							mpLinks[b][m].push_back(BoneVertexLink(b,i,1.f));
							break;
						}
					}
				}			
			}
		}
	}

	////int numMeshes((int)pmodel->m_meshes.size());
	//m_pweights = new vector<float>[numMeshes];	//allocate memory for the weights
	////int numv=(int)pmodel->m_meshes[0].m_vertices.size();
	////
	//// Walk the bone's links, accumulating vertex weight factors.
	//for(m=0; m<numMeshes; m++)
	//{
	//	int numv((int)pmodel->m_meshes[m].m_vertices.size());
	//	m_pweights[m].reserve(numv);

	//	for(int bone=0; bone<mBoneCount; bone++)
	//	{
	//		int size = (int)mpLinks[bone][m].size();
	//		for (int l=0; l<size; l++)
	//		{
	//			const BoneVertexLink &link = mpLinks[bone][m][l];
	//			m_pweights[m][link.m_vertexIndex] += link.m_weight;
	//		}
	//	}
	//}

	delete [] absLengthPerSegment;
}




/*
 *	fully draw the shark, bones and textured body, mesh by mesh
 *	first apply transforms to the vertices by the bones
 *	use vertex arrays for optimal speed
 */
void Fish::DrawWithGLElements(const FishModel* pmodel)
{
	glPushMatrix();
	
	bool hasTexture=false;
	if(m_bShowBones)
		DrawBones();

	glColor3f(1,1,1);	// draw the rest white

	//
	// Wireframe?
	// save the current polygon state so we can restore it, then change
	// it to draw the tentacle
	glPushAttrib(GL_POLYGON_BIT|GL_ENABLE_BIT | GL_CURRENT_BIT);	
	if( mbShowWireframe )
	{
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
		glDisable( GL_CULL_FACE );
	}
	else
	{
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
		glEnable( GL_CULL_FACE );
	}


	//
	// apply the material
	// the shark has only one material
	const FishModel::Material& mat = pmodel->m_materials[0];

	glMaterialfv(GL_FRONT,	GL_AMBIENT,		mat.m_ambient);
	glMaterialfv(GL_FRONT,	GL_DIFFUSE,		mat.m_diffuse);
	glMaterialfv(GL_FRONT,	GL_SPECULAR,	mat.m_specular);
	glMaterialfv(GL_FRONT,	GL_EMISSION,	mat.m_emissive);
	glMaterialf (GL_FRONT,	GL_SHININESS,	mat.m_shininess);

	glColor4fv(mat.m_diffuse);

	if(mat.m_texture == 0)
	{
		glDisable(GL_TEXTURE_2D);
	}
	else
	{
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D,mat.m_texture);
		glEnableClientState( GL_TEXTURE_COORD_ARRAY );
		hasTexture=true;		
	}


	//
	// create temporary buffers to keep the mesh data
	// make them big enough to hold the biggest mesh
	int maxv = pmodel->maxMeshVerts;
	Vector3*	pverts = new Vector3[maxv];
	Vector3*	pnormals = new Vector3[maxv];
	Vector2*	ptexcoords = new Vector2[maxv];
	float*		pweights = new float[maxv];

	int numMeshes((int)pmodel->m_meshes.size());
	for(int m=0; m<numMeshes; m++)
	{
		const FishModel::Mesh& mesh = pmodel->m_meshes[m];

		int numVerts((int)mesh.m_vertices.size());

		//
		// copy the data from the mesh to those temporary buffers to keep
		// original mesh untouched from the transformations
		for (int v=0; v<numVerts; v++)
		{
			const FishModel::Vertex& vert = mesh.m_vertices[v];
			ptexcoords[v] = vert.m_texCoord;
			pnormals[v] = Vector3();
			pverts[v] = vert.m_location;
			pweights[v] = 0;
		}
		
		//
		// Walk the bone's links, accumulating vertex weight factors.
		// TODO this can be done only once in Build...
		for(int bone=0; bone<mBoneCount; bone++)
		{
			int size = (int)mpLinks[bone][m].size();
			for (int l=0; l<size; l++)
			{
				const BoneVertexLink &link = mpLinks[bone][m][l];
				pweights[link.m_vertexIndex] += link.m_weight;
			}
		}

		//	
		// Walk the bones, accumulating xforms.
		Bone *rootbone(GetBone(mRootIndex));
		Vector3 curEnd(rootbone->mBase);
		Quaternion curOrient(rootbone->mRotation);

		for (int bone=mRootIndex-1; bone>=0; bone--)
		{
			Bone *pBone(GetBone(bone));
			Vector3 localEnd(0,0,pBone->mLength);
			curOrient *= pBone->mRotation;
			curOrient.RotateVector(localEnd,false);

			int numLinks = (int)mpLinks[bone][m].size();	// number of links with this mesh
			for (int l=0; l<numLinks; l++)
			{
				BoneVertexLink &link = mpLinks[bone][m][l];
				Vector3 &vert = pverts[link.m_vertexIndex];
				Vector3 localDelta(vert - pBone->mBase);
				Vector3 vertDelta(localDelta);
				curOrient.RotateVector(vertDelta,false);
				vertDelta -= localDelta;

				if(pweights[link.m_vertexIndex]!=0)
				{
					pverts[link.m_vertexIndex] += 
						(vertDelta+(curEnd-pBone->mBase))*
						(link.m_weight/pweights[link.m_vertexIndex]);					
				}
			}

			curEnd += localEnd;
		}


		curEnd = rootbone->mBase;
		curOrient = rootbone->mRotation;
		for (int bone=mRootIndex+1; bone<mBoneCount; bone++)
		{
			Bone *pBone(GetBone(bone));
			Vector3 localEnd(0,0,pBone->mLength);
			curOrient *= pBone->mRotation;
			curOrient.RotateVector(localEnd,false);

			int numLinks((int)mpLinks[bone][m].size());	// number of links with this mesh
			for (int l=0; l<numLinks; l++)
			{
				BoneVertexLink &link = mpLinks[bone][m][l];

				Vector3 localDelta(
					mesh.m_vertices[link.m_vertexIndex].m_location-pBone->mBase);
				Vector3 vertDelta(localDelta);
				curOrient.RotateVector(vertDelta,false);
				vertDelta -= localDelta;

				if(pweights[link.m_vertexIndex]!=0)
				{
					pverts[link.m_vertexIndex] += 
						(vertDelta+(curEnd-pBone->mBase))*
						(link.m_weight/pweights[link.m_vertexIndex]);
				}
			}

			curEnd += localEnd;
		}
		
		

		//
		// create normals
		// Walk all the triangles, cross each one to get a face normal, 
		// add it to each vertex.
		// we don't use the normals loaded from the model
		int numTriangles((int)mesh.m_triangles.size());
		for (int tr=0; tr<numTriangles; tr++)
		{
			Vector3 &v1 = pverts[mesh.m_triangles[tr].m_vertexIndices[0]];
			Vector3 &v2 = pverts[mesh.m_triangles[tr].m_vertexIndices[1]];
			Vector3 &v3 = pverts[mesh.m_triangles[tr].m_vertexIndices[2]];

			Vector3 v4 = v2-v1;
			Vector3 v5 = v3-v1;

			Vector3 normal(Cross3(v4,v5).Normalized());
			pnormals[mesh.m_triangles[tr].m_normalIndices[0]] += normal;
			pnormals[mesh.m_triangles[tr].m_normalIndices[1]] += normal;
			pnormals[mesh.m_triangles[tr].m_normalIndices[2]] += normal;
		}

		//
		// normalize all normals
		for (int n=0; n<numVerts; n++)
			pnormals[n].Normalize();
				


		//
		// Draw stuff now.
		
		// actually, every shark model has one material only
		// so I don't check which material is with which mesh
		// I just apply the only one material and draw all vertices and normals at once
				
		if(hasTexture)		
			glTexCoordPointer(2,GL_FLOAT,sizeof(Vector2),&ptexcoords[0].x);


		//
		// normals
		glEnableClientState( GL_NORMAL_ARRAY );
		glNormalPointer(GL_FLOAT,sizeof(Vector3),&pnormals[0].x);

		// Vertexes
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(3,GL_FLOAT,sizeof(Vector3),&pverts[0].x);

		glDrawElements(GL_TRIANGLES,(int)mesh.m_triangles.size(),
			GL_UNSIGNED_INT, mesh.m_triangles[0].m_vertexIndices);
		//glDrawArrays(GL_TRIANGLES,0,(int)pmodel->m_triangles.size());



		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_COLOR_ARRAY);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);

		
	}
	glPopAttrib();

	//
	// clear those stuff 
	delete[] pverts;
	delete[] pnormals;
	delete[] ptexcoords;
	delete[] pweights;

	pverts=NULL;
	pnormals=NULL;
	ptexcoords=NULL;
	pweights=NULL;

	glPopMatrix();
}



/*
 *	draw the model with the classic glvertex calls (slow)
 *
 */
void Fish::Draw(const FishModel* pmodel)
{
	glPushMatrix();
	PositionalObject::Draw();

	bool hasTexture=false;
	if(m_bShowBones)
		DrawBones();

	glColor3f(1,1,1);	// draw the rest white

	//
	// Wireframe?
	// save the current polygon state so we can restore it, then change
	// it to draw the tentacle
	/*glPushAttrib(GL_POLYGON_BIT|GL_ENABLE_BIT | GL_CURRENT_BIT);	
	if( mbShowWireframe )
	{
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
		glDisable( GL_CULL_FACE );
	}
	else
	{
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
		glEnable( GL_CULL_FACE );
	}*/

	//
	// apply the one and only material of the shark
	const FishModel::Material &mat = pmodel->m_materials[0];
	glMaterialfv(GL_FRONT,	GL_AMBIENT,		mat.m_ambient);
	glMaterialfv(GL_FRONT,	GL_DIFFUSE,		mat.m_diffuse);
	glMaterialfv(GL_FRONT,	GL_SPECULAR,	mat.m_specular);
	//glMaterialfv(GL_FRONT,	GL_EMISSION,	mat.m_emissive);
	//glMaterialf (GL_FRONT,	GL_SHININESS,	mat.m_shininess);

	//glColor4fv(mat.m_diffuse);
	//glColor3f(mat.m_diffuse[0],mat.m_diffuse[1],mat.m_diffuse[2]);

	if(mat.m_texture == 0)
	{
		glDisable(GL_TEXTURE_2D);
	}
	else
	{
		if(GLEW_ARB_multitexture)
		{
			glActiveTextureARB(GL_TEXTURE0);
		}
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D,mat.m_texture);
		hasTexture=true;				
	}

	//
	// create temporary buffers to keep the mesh data
	// make them big enough to hold the biggest mesh
	int maxv = pmodel->maxMeshVerts;
	Vector3*	pverts = new Vector3[maxv];
	Vector3*	pnormals = new Vector3[maxv];
	Vector2*	ptexcoords = new Vector2[maxv];
	float*		pweights = new float[maxv];

	//
	// process meshes one by one
	int numMeshes((int)pmodel->m_meshes.size());
	for (int m=0; m<numMeshes; m++)
	{
		const FishModel::Mesh& mesh = pmodel->m_meshes[m];

		int numVerts((int)mesh.m_vertices.size());
		
		//
		// copy the data from the mesh to those temporary buffers to keep
		// original mesh untouched from the transformations
		/*pverts = new Vector3[numVerts];
		pnormals = new Vector3[numVerts];
		ptexcoords = new Vector2[numVerts];
		pweights = new float[numVerts];*/
		for (int v=0; v<numVerts; v++)
		{
			const FishModel::Vertex& vert = mesh.m_vertices[v];
			ptexcoords[v] = vert.m_texCoord;
			pnormals[v] = Vector3();
			pverts[v] = vert.m_location;
			pweights[v] = 0;
		}


		
		//
		// Walk the bone's links, accumulating vertex weight factors.
		// TODO this can be done only once in Build...
		for(int bone=0; bone<mBoneCount; bone++)
		{
			int size = (int)mpLinks[bone][m].size();
			for (int l=0; l<size; l++)
			{
				const BoneVertexLink &link = mpLinks[bone][m][l];
				pweights[link.m_vertexIndex] += link.m_weight;
			}
		}

		//	
		// Walk the bones, accumulating xforms.
		Bone *rootbone(GetBone(mRootIndex));
		Vector3 curEnd(rootbone->mBase);
		Quaternion curOrient(rootbone->mRotation);

		for (int bone=mRootIndex-1; bone>=0; bone--)
		{
			Bone *pBone(GetBone(bone));
			Vector3 localEnd(0,0,pBone->mLength);
			curOrient *= pBone->mRotation;
			curOrient.RotateVector(localEnd,false);

			int numLinks = (int)mpLinks[bone][m].size();	// number of links with this mesh
			for (int l=0; l<numLinks; l++)
			{
				BoneVertexLink &link = mpLinks[bone][m][l];
				Vector3 &vert = pverts[link.m_vertexIndex];
				Vector3 localDelta(vert - pBone->mBase);
				Vector3 vertDelta(localDelta);
				curOrient.RotateVector(vertDelta,false);
				vertDelta -= localDelta;

				if(pweights[link.m_vertexIndex]!=0)
				{
					pverts[link.m_vertexIndex] += 
						(vertDelta+(curEnd-pBone->mBase))*
						(link.m_weight/pweights[link.m_vertexIndex]);					
				}
			}

			curEnd += localEnd;
		}
	

		curEnd = rootbone->mBase;
		curOrient = rootbone->mRotation;
		for (int bone=mRootIndex+1; bone<mBoneCount; bone++)
		{
			Bone *pBone(GetBone(bone));
			Vector3 localEnd(0,0,pBone->mLength);
			curOrient *= pBone->mRotation;
			curOrient.RotateVector(localEnd,false);

			int numLinks((int)mpLinks[bone][m].size());	// number of links with this mesh
			for (int l=0; l<numLinks; l++)
			{
				BoneVertexLink &link = mpLinks[bone][m][l];

				Vector3 localDelta(	pverts[link.m_vertexIndex]-pBone->mBase);
				Vector3 vertDelta(localDelta);
				curOrient.RotateVector(vertDelta,false);
				vertDelta -= localDelta;

				if(pweights[link.m_vertexIndex]!=0)
				{
					pverts[link.m_vertexIndex] += 
						(vertDelta+(curEnd-pBone->mBase))*
						(link.m_weight/pweights[link.m_vertexIndex]);					
				}
			}
			curEnd += localEnd;
		}



		//
		// create normals
		// Walk all the triangles, cross each one to get a face normal, 
		// add it to each vertex.
		// we don't use the normals loaded from the model
		int numTriangles((int)mesh.m_triangles.size());
		for (int tr=0; tr<numTriangles; tr++)
		{
			Vector3 &v1 = pverts[mesh.m_triangles[tr].m_vertexIndices[0]];
			Vector3 &v2 = pverts[mesh.m_triangles[tr].m_vertexIndices[1]];
			Vector3 &v3 = pverts[mesh.m_triangles[tr].m_vertexIndices[2]];

			Vector3 v4 = v2-v1;
			Vector3 v5 = v3-v1;

			Vector3 normal(Cross3(v4,v5).Normalized());
			pnormals[mesh.m_triangles[tr].m_normalIndices[0]] += normal;
			pnormals[mesh.m_triangles[tr].m_normalIndices[1]] += normal;
			pnormals[mesh.m_triangles[tr].m_normalIndices[2]] += normal;
		}

		//
		// normalize all normals
		for (int n=0; n<numVerts; n++)
			pnormals[n].Normalize();
		
	

		//
		// Draw the mesh now...
		glBegin(GL_TRIANGLES);
		{
			/*Vector3* vec;
			Vector3* norm;
			Vector2* texc;*/
			for (int t=0; t<numTriangles; t++)
			{
				for (int v=0; v<3; v++)
				{
					//vec = &pverts[mesh.m_triangles[t].m_vertexIndices[v]];
					//norm = &pnormals[mesh.m_triangles[t].m_normalIndices[v]];
					//texc = &ptexcoords[mesh.m_triangles[t].m_vertexIndices[v]];

					glTexCoord2(ptexcoords[mesh.m_triangles[t].m_vertexIndices[v]]);
					glNormal3(pnormals[mesh.m_triangles[t].m_normalIndices[v]]);
					glVertex3(pverts[mesh.m_triangles[t].m_vertexIndices[v]]);
				}
			}
		}
		glEnd();		
	}

//	glPopAttrib();


	//
	// clear those stuff 
	delete[] pverts;
	delete[] pnormals;
	delete[] ptexcoords;
	delete[] pweights;

	pverts=NULL;
	pnormals=NULL;
	ptexcoords=NULL;
	pweights=NULL;

	glPopMatrix();
}



/*
 *	draw the model as shadow
 *	here I suppose that the modelview has already be multiplied by a shadow matrix
 *	here we draw the model without materials, normals etc....
 *
 */
void Fish::DrawShadow(const FishModel* pmodel)
{
	glPushMatrix();
	PositionalObject::Draw();		
	//glTranslate(m_position);	// just translate, no rotate


	//glColor4f(0,0,.2f,.0f);
	
	//
	// create temporary buffers to keep the mesh data
	// make them big enough to hold the biggest mesh
	int maxv = pmodel->maxMeshVerts;
	Vector3*	pverts = new Vector3[maxv];
	float*		pweights = new float[maxv];
	
	//
	// process meshes one by one
	int numMeshes((int)pmodel->m_meshes.size());
	for (int m=0; m<numMeshes; m++)
	{
		const FishModel::Mesh& mesh = pmodel->m_meshes[m];

		int numVerts((int)mesh.m_vertices.size());

		//
		// copy the data from the mesh to those temporary buffers to keep
		// original mesh untouched from the transformations
		for (int v=0; v<numVerts; v++)
		{
			const FishModel::Vertex& vert = mesh.m_vertices[v];
			pverts[v] = vert.m_location;
			pweights[v] = 0;
		}



		//
		// Walk the bone's links, accumulating vertex weight factors.
		// TODO this can be done only once in Build...
		for(int bone=0; bone<mBoneCount; bone++)
		{
			int size = (int)mpLinks[bone][m].size();
			for (int l=0; l<size; l++)
			{
				const BoneVertexLink &link = mpLinks[bone][m][l];
				pweights[link.m_vertexIndex] += link.m_weight;
			}
		}

		//	
		// Walk the bones, accumulating xforms.
		Bone *rootbone(GetBone(mRootIndex));
		Vector3 curEnd(rootbone->mBase);
		Quaternion curOrient(rootbone->mRotation);

		for (int bone=mRootIndex-1; bone>=0; bone--)
		{
			Bone *pBone(GetBone(bone));
			Vector3 localEnd(0,0,pBone->mLength);
			curOrient *= pBone->mRotation;
			curOrient.RotateVector(localEnd,false);

			int numLinks = (int)mpLinks[bone][m].size();	// number of links with this mesh
			for (int l=0; l<numLinks; l++)
			{
				BoneVertexLink &link = mpLinks[bone][m][l];
				Vector3 &vert = pverts[link.m_vertexIndex];
				Vector3 localDelta(vert - pBone->mBase);
				Vector3 vertDelta(localDelta);
				curOrient.RotateVector(vertDelta,false);
				vertDelta -= localDelta;

				if(pweights[link.m_vertexIndex]!=0)
				{
					pverts[link.m_vertexIndex] += 
						(vertDelta+(curEnd-pBone->mBase))*
						(link.m_weight/pweights[link.m_vertexIndex]);					
				}
			}

			curEnd += localEnd;
		}


		curEnd = rootbone->mBase;
		curOrient = rootbone->mRotation;
		for (int bone=mRootIndex+1; bone<mBoneCount; bone++)
		{
			Bone *pBone(GetBone(bone));
			Vector3 localEnd(0,0,pBone->mLength);
			curOrient *= pBone->mRotation;
			curOrient.RotateVector(localEnd,false);

			int numLinks((int)mpLinks[bone][m].size());	// number of links with this mesh
			for (int l=0; l<numLinks; l++)
			{
				BoneVertexLink &link = mpLinks[bone][m][l];

				Vector3 localDelta(	pverts[link.m_vertexIndex]-pBone->mBase);
				Vector3 vertDelta(localDelta);
				curOrient.RotateVector(vertDelta,false);
				vertDelta -= localDelta;

				if(pweights[link.m_vertexIndex]!=0)
				{
					pverts[link.m_vertexIndex] += 
						(vertDelta+(curEnd-pBone->mBase))*
						(link.m_weight/pweights[link.m_vertexIndex]);					
				}
			}
			curEnd += localEnd;
		}



		//
		// --NO normals
		int numTriangles((int)mesh.m_triangles.size());
		
		//
		// Draw the mesh now...
		glBegin(GL_TRIANGLES);
		{
			for (int t=0; t<numTriangles; t++)
			{
				for (int v=0; v<3; v++)
				{
					glVertex3(pverts[mesh.m_triangles[t].m_vertexIndices[v]]);
				}
			}
		}
		glEnd();		
	}

	//glPopAttrib();


	//
	// clear those stuff 
	delete[] pverts;	
	delete[] pweights;

	pverts=NULL;
	pweights=NULL;

	glPopMatrix();
}








/*
 *	return the shark's bounding sphere shifted by the shark's position
 *	i.e. the bsphere in world coordinates
 */
BoundingSphere Fish::GetWorldBoundingSphere() const
{
	Vector3 pos = GetPosition();			// get shark's position
	BoundingSphere bs(mBoundingSphere);	// copy shark's bsphere into bs
	bs.m_center += pos;						// shift bs's center by pos
	return bs;								// return bs
}

/*
 *	draw only the bones of the shark
 *	
 */
void Fish::DrawBones()
{
	//
	// this actually positions the system
	PositionalObject::Draw();


	glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT);
	glDepthFunc( GL_ALWAYS );
	glDisable( GL_LIGHTING );
	glDisable(GL_DEPTH_TEST);
	glColor3f( 1.0, 0.5, 0.5 );
	
	
	Bone *pbone(GetBone(mRootIndex));
	Vector3 prevEnd(pbone->mBase);
	Vector3 curEnd(prevEnd);
	
	Quaternion curOrient;
	Vector3 localEnd;
	
	glColor3f(1,0,0);	
	//
	// draw a red line strip from the root to the head
	glBegin( GL_LINE_STRIP );
	{
			

		glVertex3(prevEnd);

		for (int b=mRootIndex-1; b>=0; b--)
		{
			pbone = GetBone(b);
			curOrient *= pbone->mRotation;
			localEnd.Set(0.0f,0.0f,pbone->mLength);
			curOrient.RotateVector(localEnd,false);
			curEnd += localEnd;
			glVertex3(curEnd);
		}
	}
	glEnd();


	//
	// then, draw a green line strip from the root to the tail
	curEnd = prevEnd;
	curOrient.Identity();
	glColor3f(0,1,0);
	glBegin( GL_LINE_STRIP );
	{
		glVertex3(prevEnd);

		for (int b=mRootIndex+1; b<mBoneCount; b++)
		{
			pbone = GetBone(b);
			curOrient *= pbone->mRotation;
			localEnd.Set(0.0f,0.0f,pbone->mLength);
			curOrient.RotateVector(localEnd,false);
			curEnd += localEnd;
			glVertex3(curEnd);
		}
	}
	glEnd();

	glPopAttrib();
}

/*
 *	simple animation
 *	just set the rotation of every bone to be <angle>
 *	the bones from the root to the head will have inversed rotation from 
 *	the bones from the root to the tail
 */
void Fish::Animate1(double rads)
{
	int i;
	for (i=mRootIndex-1; i>=0; i--)
	{
		GetBone(i)->mRotation.SetFromAxis(rads,0,1,0);
	}
	for(i=mRootIndex+1; i<mBoneCount; i++)
	{
		GetBone(i)->mRotation.SetFromAxis(-rads,0,1,0);
	}
}

/*
 *	
 */
void Fish::FollowRoute(double dt)
{
	/*if(dt<=0.0 || mTimeElapsed>=mDestTime) return;
	mTimeElapsed+=dt;

	if(mTimeElapsed>=mDestTime)
	{
		SetPosition(mTarget);
		return;
	}


	float factor = float(mTimeElapsed/mDestTime);

	Vector3 p1 = mRoute.PointOnCurve(factor);
	Vector3 p2(mRoute.PointOnCurve(factor+mBezierSpeed));

	SetPosition(p1);
	PointAt(p2);*/
	
	float bezierSpeed = mSpeed/SALMON_PATH_LENGTH;
	mRouteFactor += (float)(mSpeed*dt);
	if (mRouteFactor>1.0f)
		mRouteFactor=1.0f;

	Vector3 p1 = mRoute.PointOnCurve(mRouteFactor);
	Vector3 p2(mRoute.PointOnCurve(mRouteFactor+mBezierSpeed));

	SetPosition(p1);
	PointAt(p2);

	if(mRouteFactor>=1.0f)
		mRouteFactor=0.0f;	// reset and force to re-calculate new target
}

/*
 *	
 */
void Fish::SetRoute(const Vector3& dest)
{
	/*mTimeElapsed=0.0;
	Vector3 v1(GetPosition());
	Vector3 v2(dest-v1);

	float len = v2.Length();
	len/=3;

	Vector3 p1(0,0,1);
	Quaternion rot(GetRotation());
	rot.RotateVector(p1);
	p1 *= len;
	p1 += GetPosition();

//	Vector3 p2((p1+dest)/2.f);
	Vector3 p2(((p1-dest)/2.f)+dest);

	
	mTarget=dest;


	mPrevRoute = mRoute;		// save current route for the next transition
	mRoute.Set(v1,p1,p2,dest);	// set current route
	//mRoute.Set(v1,p2,p1,dest);	*/



	/*----NEW STUFF HERE----*/

	float pathlength;
	if(GetType()==Salmon)
		pathlength=SALMON_PATH_LENGTH;
	else if(GetType()==Shark)
		pathlength=SHARK_PATH_LENGTH;
	else
		pathlength=DOLPHIN_PATH_LENGTH;


	//
	// reset inner timer
	mTimeElapsed=0.0;

	Vector3 v1(GetPosition());
	Vector3 v2(dest-v1);	
	Quaternion rot(GetRotation());
	
	//
	// the first time, there is no previous route (no movement has occurred yet)
	// so create a dummy one based on the position and orientation of the shark
	if (mbFirstTime)
	{
		Vector3 dummyCP2(0,0,pathlength*0.3f);	// dummy control point (the second one of a bezier curve)
		rot.RotateVector(dummyCP2);				// rotate it to face where the shark faces
		//dummyCP2 *= -1;							// invert it to look backward
		
		mPrevRoute.pcontrol2 = v1 - dummyCP2;
		mPrevRoute.pend = v1;					// as if the end of the previous movement was the one 
												// the shark is sitting now		
		mbFirstTime=false;
	}
	

//	ASSERT(mPrevRoute.pend == GetPosition());

	Vector3 c34 = mPrevRoute.pend - mPrevRoute.pcontrol2;

	float randlen = ( (float(rand())/RAND_MAX)*0.2f ) + 0.9f;	// random in [0.9 ... 1.1]

	//
	// calculate the 2 new control points of the current route
	Vector3 p1 = mPrevRoute.pcontrol2 + c34 * (1 + randlen);
	Vector3 p2 = ((p1 - dest)*0.5f) + dest;

	//
	// save current route for the next transition (except for the 1st time
	// then set the new values of current route
	
	mRoute.Set(v1,p1,p2,dest);
	mPrevRoute = mRoute;	

	mTarget=dest;
	
}


/*
 *	decide the action to take
 */
void Fish::Think()
{
	mCurrentAction=Cruise;	// leave it for now
}

void Fish::Act(double dt)
{
	switch (GetCurrentAction())
	{
	case Cruise:
		RandomCruise(dt);
		break;

	case Chase:

		break;

	case Evade:

		break;

	case Mate:

		break;

	default:
		RandomCruise(dt);
		break;
	}
}
//////////////////////////////////////////////////////////////////////////
void Fish::KeepTargetInsideWorld(Vector3 *thetarget) const
{
	//
	// check if the bounding sphere of the shark has been outside of the world box
	// if so, try to correct it ( pull the target to the box as much as possible)
	//BoundingBox &bb = mpContainer->mWorldBounds;
	BoundingBox &bb = mpContainer->mWorldBounds;
	BoundingSphere &bs = this->GetWorldBoundingSphere();


	if ( (bs.m_center.x + bs.m_radius) > (bb.m_center.x + bb.m_xdim) )	// sphere goes right of the box
	{
		thetarget->x -= (bs.m_center.x+bs.m_radius - (bb.m_center.x+bb.m_xdim));		
	}
	else if ( (bs.m_center.x - bs.m_radius) < (bb.m_center.x - bb.m_xdim) ) // sph. goes left of the box
	{
		thetarget->x += ((bb.m_center.x-bb.m_xdim)-(bs.m_center.x-bs.m_radius));
	}

	if ( (bs.m_center.z + bs.m_radius) > (bb.m_center.z + bb.m_zdim) )	// sphere goes in front of the box
	{
		thetarget->z -= (bs.m_center.z+bs.m_radius - (bb.m_center.z+bb.m_zdim));		
	}
	else if ( (bs.m_center.z - bs.m_radius) < (bb.m_center.z - bb.m_zdim) ) // sph. goes back of the box
	{
		thetarget->z += ((bb.m_center.z-bb.m_zdim)-(bs.m_center.z-bs.m_radius));
	}

	if ( (bs.m_center.y + bs.m_radius) > (bb.m_center.y + bb.m_ydim) )	// sphere goes above the box
	{
		thetarget->y -= (bs.m_center.y+bs.m_radius - (bb.m_center.y+bb.m_ydim))*0.5f;		
	}
	else if ( (bs.m_center.y - bs.m_radius) < (bb.m_center.y - bb.m_ydim) ) // sph. goes bellow the box
	{
		thetarget->y += ((bb.m_center.y-bb.m_ydim)-(bs.m_center.y-bs.m_radius))*0.5f;
	}
}
//////////////////////////////////////////////////////////////////////////
void Fish::AvoidOtherFishes(Vector3 *newtarget)
{
	BoundingSphere &bs = GetWorldBoundingSphere();
	
	FishManager::FishIter ibegin,iend;
	iend = mpContainer->mFishes.end();

	for ( ibegin=mpContainer->mFishes.begin(); ibegin!=iend; ibegin++ )
	{
		if( (*ibegin)==this )
			continue;	//?????????
		
		BoundingSphere &bs2 = (*ibegin)->GetWorldBoundingSphere();

		bool boundinSpheresCollide = bs.Collides(bs2);
		
		if(boundinSpheresCollide)
		{
			
			Vector3 diff(bs.m_center-bs2.m_center);
			float rad_diff = bs.m_radius + bs2.m_radius - diff.Length();
			diff.Normalize();
			diff *= rad_diff;
			
			(*newtarget).x += diff.x;	// try to get away -- a little
			(*newtarget).z += diff.z;
			(*newtarget).y += diff.y*0.2f;
		}		
	}
}
//////////////////////////////////////////////////////////////////////////
/**
 	\brief Set type of the fish

	Can be
	\li Shark
	\li Dolphin
	\li Salmon
	
	\todo Add more types sometime
 */
//void Fish::SetType( Fish::Type type )
//{
//	mType = type;
//}
//////////////////////////////////////////////////////////////////////////
/**
 	\brief returns the type of the fish

	Can be
	\li Shark
	\li Dolphin
	\li Salmon

	\todo Add more types sometime

 */
//Fish::Type Fish::GetType() const
//{
//	return mType;
//}
//////////////////////////////////////////////////////////////////////////
/**
 	Determine if this fish can see a specific other fish
	Returns distance between fishes IF can see it
	or -1 if not
 */
float Fish::CanSee( Fish* fistptr )
{
//	assert(fistptr);	//make sure is not null

	// check if we are checking against ourselves
	if (this==fistptr)
		return /*INFINITE*/999999.0f;

	//determine distance
	float dist = glmath::Distance(this->GetPosition(),fistptr->GetPosition());

	if( mSeeRadius>dist )
	{
		return dist;
	}

	return -1;
}

void Fish::SelectCruiseTarget()
{
	double yangle(0.0);	//the angle to turn around y-axis
	double xangle(0.0);	//...... x-axis	
	int possibility = rand()%100;
	Vector3 thetarget(0,0,0);
	if (mType==Salmon)
	{
		//thetarget.z=1.5f;
		thetarget.z = SALMON_PATH_LENGTH;
	}
	else if (mType==Shark)
	{
		//thetarget.z=5.0f;
		thetarget.z = SHARK_PATH_LENGTH;
	}
	else
	{
		//thetarget.z=4.0f;
		thetarget.z = DOLPHIN_PATH_LENGTH;
	}

	//Vector3 thetarget(0,0,GetWorldBoundingSphere().m_radius);
	Quaternion rotTarget(GetRotation());

	/*BoundingBox &bb = mpContainer->mWorldBounds;*/

	//
	//find the angles of the new target
	if(possibility<4)
	{
		xangle = Deg2Rad(float(rand()%4));
	}
	else if(possibility<88)
	{
		yangle = Deg2Rad(float(rand()%35));
	}
	else if(possibility<95)
	{
		yangle = Deg2Rad(float(rand()%50));
	}
	else
	{
		xangle = Deg2Rad(float(rand()%6));
		yangle = Deg2Rad(float(rand()%60));
	}

	//
	// find which angles will be inverted
	// positive yangle means turn left 
	// if the shark is left-eyed, there is higher possibility to leave the angle positive
	possibility = rand()%100;




#ifdef SHARK_CYCLIC_PATHS	
	// take into consideration the preference of the shark

	if(mbLeftEyed)
	{
		if(possibility<20)
			//yangle = -yangle;
			yangle *= -1;	// so its negative
	}
	else	//right-eyed
	{
		if(possibility<80)
			yangle *= -1;
	}

#else

	// equal possibility for both sides
	if(possibility<50 )
	{
		yangle = -yangle;
	}

#endif


	if(possibility<25 || possibility>75)
		xangle = -xangle;



	rotTarget *= Quaternion().SetFromRadians(xangle,yangle,0);
	rotTarget.RotateVector(thetarget);
	thetarget += GetPosition();

	////
	//// check if the bounding sphere of the shark has been outside of the world box
	//// if so, try to correct it ( pull the target to the box as much as possible)
	//BoundingSphere &bs = this->GetWorldBoundingSphere();


	KeepTargetInsideWorld(&thetarget);
	AvoidOtherFishes(&thetarget);	


	// check is target is inside some logical bounds.....

	SetRoute(thetarget);

#if 0

	// debug the route we just set
	Log("fish type: %d",(GetType()));
	Log("fish pos: [%f %f %f]",m_position.x,m_position.y,m_position.z);
	Log("route:");
	Vector3 &vb = m_route.pstart;
	Log("begin: [%f %f %f]",vb.x,vb.y,vb.z);
	vb = m_route.pend;
	Log("end: [%f %f %f]",vb.x,vb.y,vb.z);
	vb = m_route.pcontrol1;
	Log("control pnt1: [%f %f %f]",vb.x,vb.y,vb.z);
	vb = m_route.pcontrol2;
	Log("control pnt2: [%f %f %f]",vb.x,vb.y,vb.z);


#endif

	mRouteFactor=0;
}

void Fish::RandomCruise( double dt )
{
	double angle;

	// set speed at minimum
	mSpeed = mMinSpeed;

	if(mRouteFactor<=0)
		SelectCruiseTarget();
	FollowRoute(dt);
	
	angle = 0.08*PIdiv3*sin(mAngleFactor);		
	Animate1(angle);

	float random = float(rand()%180);	// a random number 0..179 
	mAngleFactor += 2*dt*PIdiv2 + dt*PIdiv2*.4*sin(Deg2Rad(random));
	if(mAngleFactor>TWO_PI)
		mAngleFactor-=TWO_PI;
}


/*
 *	just clear...
 */
FishManager::FishManager()
{
	Clear();
}

/*
 *	just clear...
 */
FishManager::~FishManager()
{
	Clear();
}

/*
 *	initialize the manager (populate the shark vector)
	This one is called only if there is no .ini file to initialize the world
 */
void FishManager::Init()
{
	FishModel *pmodel = new FishModel;
	pmodel->LoadFromAsciiMilkshape(MODELNAME1);
	mModels[MODELNAME1] = pmodel;
	
	FishModel *pmodel2 = new FishModel;
	pmodel2->LoadFromAsciiMilkshape(MODELNAME2);
	mModels[MODELNAME2] = pmodel2;

	float lengths[] = {1,1,1,1,1,1,1};
	Fish *pshark = new Fish(this);
	pshark->Build(mModels[MODELNAME1],MODELNAME1,7,3,lengths);
	pshark->ShowBones(false);
	pshark->SetPosition(Vector3(-1,0,0));
	mFishes.push_back(pshark);

	float lengths2[] = {1,1,1,1,1,1,1,1,1};
	Fish *pshark2 = new Fish(this);
	pshark2->Build(mModels[MODELNAME2],MODELNAME2,9,4,lengths2);
	pshark2->ShowBones(false);
	pshark2->SetPosition(Vector3(1,0,0));
	mFishes.push_back(pshark2);
}


/*
 *	
 */
void FishManager::Clear()
{
	//
	// delete sharkies and clear vector
	for(FishIter ishark=mFishes.begin();
				ishark != mFishes.end(); ishark++)
	{
		delete (*ishark);
	}
	mFishes.clear();

	//
	// delete models and clear map
	for(ModelIter imod=mModels.begin();
			imod!=mModels.end(); imod++)
	{
		delete (*imod).second;
	}
	mModels.clear();
}


/*
 *	
 */
void FishManager::Update(double dt)
{
	//static double anglefactor = 0.0;
	//double angle = 0.15*PIdiv3*sin(anglefactor);
	//Vector3 yaxis(0,1,0);
	
	//double angle;

	for(FishIter ishark = mFishes.begin();
					ishark!=mFishes.end(); ishark++)
	{
	
// 		if((*ishark)->mTimeElapsed>=(*ishark)->mDestTime)
// 		{
// //			do 
// 			{	
// 				// think until you find a new target INSIDE the world bounds 
// 				(*ishark)->Think();	
// 			} 
// //			while( mWorldBounds.ClassifyPoint((*ishark)->mTarget*2.f) == BoundingBox::POINT_OUTSIDE_BOX );			
// 		}
// 		else
// 		{
// 			//
// 			// check if the shark collides with any other shark
// 			//for (FishIter ishark2=mFishes.begin(); ishark2!=mFishes.end(); ishark2++)
// 			//{
// 			//	if(ishark2==ishark)
// 			//		continue;
// 	
// 			//	BoundingSphere &bs = (*ishark)->GetWorldBoundingSphere();
// 			//	BoundingSphere &bs2= (*ishark2)->GetWorldBoundingSphere();
// 
// 			//	if(bs.Collides(bs2))
// 			//	{
// 			//		(*ishark)->Think();
// 			//		break;	// dont think again, leave the other sharks think to...
// 			//	}
// 			//}
// 		}
		(*ishark)->Think();
		(*ishark)->Act(dt);
		
	}
	
	
	/*anglefactor += 2*dt*PIdiv2;
	if(anglefactor>TWO_PI)
		anglefactor-=TWO_PI;*/	

}



/*
 *	draw all the sharks
 */
#define SHADOW_OFFSET 0.1f	// offset between ground and shadow
void FishManager::DrawSharks()
{


	glPushAttrib(GL_POLYGON_BIT|GL_ENABLE_BIT);	
	if( *mpbShowWireframe )
	{
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
		glDisable( GL_CULL_FACE );
	}
	else
	{
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
		glEnable( GL_CULL_FACE );
	}

	for(FishIter ishark = mFishes.begin();
		ishark!=mFishes.end(); ishark++)
	{
		if (*mpbEnableCulling)
		{
			//
			// if culling enabled, check first the bounding sphere of the shark
			// and if it is inside the frustum, then draw it
			if(mpFrustum->IsBoundingSphereInside((*ishark)->GetWorldBoundingSphere()))
				(*ishark)->Draw(mModels[(*ishark)->GetModelName()]);		
				//(*ishark)->DrawWithGLElements(mModels[(*ishark)->mModelName]);
		}
		else
		{
			//
			// draw the shark without cull check
			(*ishark)->Draw(mModels[(*ishark)->GetModelName()]);		
		}
	}
	glPopAttrib();	// for the wireframe thing...
	if (*mpbShowSharkShadows)
	{
		//
		// Get ready to draw the shadows
		// First disable lighting/texturing and save the projection state
		glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT);
		//glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glPushMatrix();

		//
		// -- no Wireframe for shadow
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
		glEnable( GL_CULL_FACE );

		glColor3fv(mSharkShadowColor);
		//
		// create the shadow matrix using 3 plane points and current light position
		Matrix shadowmat;
		Vector3 planepoints[] = { 
			Vector3(1,*mpPlaneHeight+SHADOW_OFFSET,-1), 
			Vector3(-1,*mpPlaneHeight+SHADOW_OFFSET,-1), 
			Vector3(0,*mpPlaneHeight+SHADOW_OFFSET,1)	};
		shadowmat.CreateShadowMatrix(planepoints,mpLightPos);

		//
		// Multiply by shadow projection matrix
		glMultMatrix(shadowmat);
		for(FishIter ishark = mFishes.begin();
			ishark!=mFishes.end(); ishark++)
		{
			if (*mpbEnableCulling)
			{
				//
				// if culling enabled, check first the bounding sphere of the shark
				// and if it is inside the frustum, then draw it
				if(mpFrustum->IsBoundingSphereInside((*ishark)->GetWorldBoundingSphere()))
					(*ishark)->DrawShadow(mModels[(*ishark)->GetModelName()]);
			}
			else
			{
				(*ishark)->DrawShadow(mModels[(*ishark)->GetModelName()]);		
			}
		}

		//
		// now draw the sharks again

		//
		// Restore the projection to normal
		glPopMatrix();
		glPopAttrib();
	}

	if(*mpbShowBoundingSpheres)
	{
		glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT|GL_POLYGON_BIT);
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_COLOR_MATERIAL);

		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
		glDisable( GL_CULL_FACE );

		if (GLEW_ARB_multitexture && mpbMultitexture)
		{
			glActiveTextureARB(GL_TEXTURE1_ARB);
			glDisable(GL_TEXTURE_2D);
			glActiveTextureARB(GL_TEXTURE0_ARB);
		}

		glColor3f(1,0,0);

		for(FishIter ishark = mFishes.begin();
			ishark!=mFishes.end(); ishark++)
		{
			BoundingSphere bs = (*ishark)->GetWorldBoundingSphere();
			DrawSphere(bs);
		}

		glPopAttrib();
	}
	if(*mpbShowPaths)
	{
		glPushAttrib(GL_ENABLE_BIT);
		if (GLEW_ARB_multitexture && mpbMultitexture)
		{
			glActiveTextureARB(GL_TEXTURE1_ARB);
			glDisable(GL_TEXTURE_2D);
			glActiveTextureARB(GL_TEXTURE0_ARB);
		}
		for(FishIter ishark = mFishes.begin();
			ishark!=mFishes.end(); ishark++)
		{
			BezierCurve &bs = (*ishark)->GetRoute();
			DrawBezierCurve(bs,true);
		}
		glPopAttrib();
	}
}


/*
 *	
 */
Fish* FishManager::GetFish(int index)
{
	if(index<0 || index>=GetSharkCount())
		return NULL;
	return mFishes[index];
}



void FishManager::CheckForInitialCollisions()
{
	FishIter ishark1,ishark2,iend;
	iend = mFishes.end();

	for (ishark1=mFishes.begin(); ishark1!=iend-1; ishark1++)
	{
		for (ishark2=ishark1+1; ishark2!=iend; ishark2++)
		{
			
			BoundingSphere bs1 = (*ishark1)->GetWorldBoundingSphere();
			BoundingSphere bs2 = (*ishark2)->GetWorldBoundingSphere();


			if (bs1.Collides(bs2))
			{
				
				Vector3 diff(bs1.m_center - bs2.m_center);
				float rad_diff = bs1.m_radius + bs2.m_radius - diff.Length();
				diff.Normalize();
				diff *= rad_diff;
				
				Vector3 pos = (*ishark1)->GetPosition();	
				pos += diff;
				(*ishark1)->SetPosition(pos);
			}
		}			
	}
		

}



/*
 *	draw a bezier curve as a line	
 *	optionally, draw the control points of the curve
 */
void DrawBezierCurve(const BezierCurve& bc, bool bShowControlPoints)
{
	float step(1.0f/25.0f);
	Vector3 point;

	glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT|GL_LINE_BIT);

	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	glDisable(GL_COLOR_MATERIAL);
	glLineWidth(3.0f);

	glColor3f(0,1,0);	// draw green line
	glBegin(GL_LINE_STRIP);
	for(float i=0; i<=1; i+=step)
	{
		point = bc.PointOnCurve(i);
		glVertex3(point);
	}
	glEnd();
	
	if(bShowControlPoints)
	{
		glColor3f(1,0,0);	// draw red balls
		DrawSphere(bc.pstart,.1f);
		DrawSphere(bc.pcontrol1,.1f);
		DrawSphere(bc.pcontrol2,.1f);
		DrawSphere(bc.pend,.1f);		
	}

	//glLineWidth(1.0f);
	glPopAttrib();
}

/*
 *	just draw a sphere in the specified position with specified radius
 */
void DrawSphere(const Vector3& pos, float radius)
{
	GLUquadricObj* pSphere = gluNewQuadric();
	glPushMatrix();
	glTranslate(pos);
	gluSphere(pSphere,radius,15,15);
	glPopMatrix();
	gluDeleteQuadric(pSphere);
}


void DrawSphere(const BoundingSphere& bs)
{
	DrawSphere(bs.m_center,bs.m_radius);
}

void DrawSphere_colored( const BoundingSphere& bs, float r/*=1*/,float g/*=0*/, float b/*=0*/ )
{
	glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT|GL_LINE_BIT);

	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	glDisable(GL_COLOR_MATERIAL);
	glLineWidth(2.5f);

	glColor3f(r,g,b);	// set color

	GLUquadricObj* pSphere = gluNewQuadric();
	glPushMatrix();
	glTranslate(bs.GetCenter());
	gluSphere(pSphere,bs.GetRadius(),15,15);
	glPopMatrix();
	gluDeleteQuadric(pSphere);

	glPopAttrib();
}

// void DrawSphere( const BoundingSphere& bs, float r/*=1*/,float g/*=0*/, float b/*=0*/ )
// {
// 	
// }
void DrawBoundingBox( const glmath::BoundingBox& bb, float r/*=0*/, float g/*=1*/,float b/*=0*/ )
{
	const Vector3 &center = bb.GetCenter();
	const float x = bb.GetXDim();
	const float y = bb.GetYDim();
	const float z = bb.GetZDim();

	glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT|GL_LINE_BIT);

	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	glDisable(GL_COLOR_MATERIAL);
	glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
	glDisable(GL_CULL_FACE);
	glLineWidth(2.0f);
	glColor3f(r,g,b);	 //set color

	glPushMatrix();
	glTranslate( center);
	glBegin(GL_QUADS);
		
		// front
		glVertex3f(  x,  y, z  );
		glVertex3f( -x,  y, z  );
		glVertex3f( -x, -y, z  );
		glVertex3f(  x, -y, z  );

		// back
		glVertex3f(  x, -y, -z  );
		glVertex3f( -x, -y, -z  );
		glVertex3f( -x,  y, -z  );
		glVertex3f(  x,  y, -z  );

		// top
		glVertex3f(  x,  y,  -z  );
		glVertex3f( -x,  y,  -z  );
		glVertex3f( -x,  y,   z  );
		glVertex3f(  x,  y,   z  );

		// bottom
		glVertex3f(  x, -y,  z  );
		glVertex3f( -x, -y,  z  );
		glVertex3f( -x, -y, -z  );
		glVertex3f(  x, -y, -z  );

		// left
		glVertex3f( -x,  y,  z  );
		glVertex3f( -x,  y, -z  );
		glVertex3f( -x, -y, -z  );
		glVertex3f( -x, -y,  z  );

		// right
		glVertex3f(  x,  y, -z  );
		glVertex3f(  x,  y,  z  );
		glVertex3f(  x, -y,  z  );
		glVertex3f(  x, -y, -z  );

	glEnd();
	glPopMatrix();

	glPopAttrib();

}

void DrawRay( const glmath::Ray& ray, float length,float r/*=1*/,float g/*=1*/,float b/*=0*/ )
{
	glPushAttrib(GL_ENABLE_BIT|GL_CURRENT_BIT|GL_LINE_BIT);

	glDisable(GL_TEXTURE_2D);
	glDisable(GL_LIGHTING);
	glDisable(GL_COLOR_MATERIAL);
	glLineWidth(2.5f);

	glColor3f(r,g,b);	// set color

	const Vector3& rayorig = ray.GetOrigin();
	Vector3 direction = (ray.GetDirection()-ray.GetOrigin()).Normalized();
	
	glBegin(GL_LINES);
	{	
		glVertex3(ray.GetOrigin());
		glVertex3(ray.GetDirection());
	}	
	glEnd();

	glPopAttrib();
}


